package cn.assumejson.springframework.util;

import cn.assumejson.springframework.beans.factory.BeansException;
import cn.assumejson.springframework.beans.factory.annotation.Autowired;
import cn.assumejson.springframework.beans.factory.annotation.Qualifier;
import cn.assumejson.springframework.beans.factory.annotation.Value;
import cn.assumejson.springframework.beans.factory.support.DefaultListableBeanFactory;
import cn.hutool.core.bean.BeanUtil;
import lombok.extern.slf4j.Slf4j;

import java.beans.Introspector;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 属性解析器
 *
 * @author Mr. Li
 * @version 1.0
 * @since 1.0 2021/12/3 21:28
 */
@Slf4j
public class ResolvableTypeUtil {


    public static void parsingAttributes(Object bean, DefaultListableBeanFactory beanFactory) throws InvocationTargetException, IllegalAccessException {
        // 1. 处理注解@Value
        Class<?> clazz = bean.getClass();
        // 如果是cglib 生成的Bean，则需要获取其父类，就是target
        clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;

        resolveValue(bean, clazz, beanFactory);

        // 解析 @Autowire
        resolveAutowire(bean, clazz, beanFactory);
    }

    private static void resolveAutowire(Object bean, Class<?> clazz, DefaultListableBeanFactory beanFactory) throws InvocationTargetException, IllegalAccessException {
        resolveValueAutowire(bean, clazz, beanFactory);
        resolveValueAutowireByMethod(bean, clazz);
    }

    private static void resolveValueAutowireByMethod(Object bean, Class<?> clazz) throws InvocationTargetException, IllegalAccessException {
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            Autowired annotation = declaredMethod.getAnnotation(Autowired.class);
            if (annotation != null) {
                declaredMethod.invoke(bean);
            }
        }
    }

    private static void resolveValueAutowire(Object bean, Class<?> clazz, DefaultListableBeanFactory beanFactory) {
        Field[] declaredFields = clazz.getDeclaredFields();
        // ========变量
        for (Field field : declaredFields) {
            Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
            if (null != autowiredAnnotation) {
                Class<?> fieldType = field.getType();
                String dependentBeanName = null;
                Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
                Object dependentBean = null;
                if (null != qualifierAnnotation) {
                    dependentBeanName = qualifierAnnotation.value();
                    // 解决循环依赖
//                    dependentBean = beanFactory.getSingleton(dependentBeanName);
                    dependentBean = beanFactory.getBean(dependentBeanName, fieldType);
                } else {
                    // 获取
//                    dependentBean = beanFactory.getSingleton(Introspector.decapitalize(field.getName()));
                    dependentBean = beanFactory.getBean(Introspector.decapitalize(field.getName()), fieldType);
                }
                // 判断返回的对象是否为代理对象
                if (Proxy.isProxyClass(dependentBean.getClass())) {
                    // 这里一般存在的情况都是 IuserService userService； 这种情况，但是也可以支持 UserService userService
                    // 获取 参数类型
                    // 1. 获取 代理对象代理的接口类型
                    if (fieldType.isInterface()) {
                        BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
                    } else {

                        log.error("{}，The dynamic proxy return type is wrong, the return type must be an interface type", field.getName());
                        throw new BeansException("The dynamic proxy return type is wrong, the return type must be an interface type");
//                        Class<?>[] interfaces = fieldType.getInterfaces();
//                        // 目前就支持一个类型吧
//                        Class<?> interFaceClass = interfaces[0];
//                        // 判断属性是否一致
//                        if (interFaceClass.getName().equals(fieldType.getName())) {
//                            try {
//                                field.set(null, dependentBean);
//                            } catch (IllegalAccessException e) {
//                                e.printStackTrace();
//                            }
//                        }
                    }
                } else {
                    BeanUtil.setFieldValue(bean, field.getName(), dependentBean);
                }
            }
        }
    }

    private static void resolveValue(Object bean, Class<?> clazz, DefaultListableBeanFactory beanFactory) {
        resolveValueAnnotation(bean, clazz, beanFactory);

        resolveValueAnnotationByMethodParameter(bean, clazz, beanFactory);
    }

    private static void resolveValueAnnotationByMethodParameter(Object bean, Class<?> clazz, DefaultListableBeanFactory beanFactory) {
        // ==== 入参
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                Value typeAnnotation = parameterType.getAnnotation(Value.class);
                if (null != typeAnnotation) {
                    String value = typeAnnotation.value();
                    value = beanFactory.resolveEmbeddedValue(value);
                    BeanUtil.setFieldValue(bean, parameterType.getName(), value);
                }
            }
        }
    }

    private static void resolveValueAnnotation(Object bean, Class<?> clazz, DefaultListableBeanFactory beanFactory) {
        // 获取所有的属性，@Value 是可以分别作用在 字段，入参上的
        Field[] declaredFields = clazz.getDeclaredFields();
        // ========变量
        for (Field field : declaredFields) {
            Value valueAnnotation = field.getAnnotation(Value.class);
            if (null != valueAnnotation) {
                String value = valueAnnotation.value();
                value = beanFactory.resolveEmbeddedValue(value);
                BeanUtil.setFieldValue(bean, field.getName(), value);
            }
        }
    }
}
